use crate::{
    ast::{types, Type, VecType},
    error::{CompilerError, Result},
    llvm_backend::LLVMBackend,
};
use inkwell::{types::BasicType, values::BasicValueEnum, AddressSpace};

impl<'ctx> LLVMBackend<'ctx> {
    /// Deep copy a slice (copy the underlying array)
    #[allow(dead_code)]
    pub(in crate::llvm_backend) fn deep_copy_slice(
        &mut self,
        source_slice: BasicValueEnum<'ctx>,
        vector_type: &VecType,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Handle case where source_slice is a pointer (from dereferencing *ptr)
        let source_struct = if source_slice.is_pointer_value() {
            // Load the slice structure from the pointer
            let slice_ptr = source_slice.into_pointer_value();
            let element_llvm_type =
                self.nrc_type_to_llvm_type(&Some(*vector_type.element_type.clone()))?;
            let slice_struct_type = self.context.struct_type(
                &[
                    element_llvm_type.ptr_type(AddressSpace::default()).into(),
                    self.context.i64_type().into(),
                    self.context.i64_type().into(),
                ],
                false,
            );
            let loaded =
                self.builder
                    .build_load(slice_struct_type, slice_ptr, "load_slice_for_copy")?;
            if !loaded.is_struct_value() {
                return Err(CompilerError::internal(
                    "Loaded value from pointer is not a struct",
                ));
            }
            loaded.into_struct_value()
        } else if source_slice.is_struct_value() {
            source_slice.into_struct_value()
        } else {
            return Err(CompilerError::internal(format!(
                "Source slice is neither struct nor pointer value: {:?}",
                source_slice
            )));
        };

        // Extract source slice fields
        let src_ptr = self
            .builder
            .build_extract_value(source_struct, 0, "src_ptr")?
            .into_pointer_value();
        let src_len = self
            .builder
            .build_extract_value(source_struct, 1, "src_len")?
            .into_int_value();
        let src_cap = self
            .builder
            .build_extract_value(source_struct, 2, "src_cap")?
            .into_int_value();

        // Get element type and size
        let element_llvm_type =
            self.nrc_type_to_llvm_type(&Some(*vector_type.element_type.clone()))?;
        let element_size = match &*vector_type.element_type {
            Type::Basic(basic_type) => match basic_type {
                types::BasicType::Int8 | types::BasicType::Uint8 | types::BasicType::Char => 1,
                types::BasicType::Int16 | types::BasicType::Uint16 => 2,
                types::BasicType::Int32 | types::BasicType::Uint32 | types::BasicType::Float32 => 4,
                types::BasicType::Int
                | types::BasicType::Int64
                | types::BasicType::Uint
                | types::BasicType::Uint64
                | types::BasicType::Float64 => 8,
                types::BasicType::Bool => 1,
                _ => 8,
            },
            Type::Rc(rc_type) => {
                if self.trait_name_from_type(&rc_type.inner_type).is_some() {
                    16
                } else {
                    8
                }
            }
            Type::Weak(weak_type) => {
                if self.trait_name_from_type(&weak_type.inner_type).is_some() {
                    16
                } else {
                    8
                }
            }
            _ => 8,
        };

        // Allocate new memory for the copy
        let element_size_const = self.context.i64_type().const_int(element_size, false);
        let new_size_bytes =
            self.builder
                .build_int_mul(src_cap, element_size_const, "new_size_bytes")?;

        let malloc_func = self
            .function_map
            .get("__malloc")
            .ok_or_else(|| CompilerError::internal("__malloc function not found"))?;
        let malloc_call =
            self.builder
                .build_call(*malloc_func, &[new_size_bytes.into()], "slice_copy_malloc")?;
        let new_ptr_i8 = malloc_call
            .try_as_basic_value()
            .left()
            .ok_or_else(|| CompilerError::internal("malloc call did not return a value"))?
            .into_pointer_value();

        let new_ptr = self
            .builder
            .build_bitcast(
                new_ptr_i8,
                element_llvm_type.ptr_type(AddressSpace::default()),
                "new_ptr_typed",
            )?
            .into_pointer_value();

        // Copy elements using memcpy
        let copy_size = self
            .builder
            .build_int_mul(src_len, element_size_const, "copy_size")?;

        // Get memcpy intrinsic
        let memcpy_intrinsic = self
            .module
            .get_function("llvm.memcpy.p0.p0.i64")
            .unwrap_or_else(|| {
                let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
                let memcpy_type = self.context.void_type().fn_type(
                    &[
                        i8_ptr_type.into(),
                        i8_ptr_type.into(),
                        self.context.i64_type().into(),
                        self.context.bool_type().into(),
                    ],
                    false,
                );
                self.module
                    .add_function("llvm.memcpy.p0.p0.i64", memcpy_type, None)
            });

        // Cast pointers to i8* for memcpy
        let dest_i8 = self
            .builder
            .build_bitcast(
                new_ptr,
                self.context.i8_type().ptr_type(AddressSpace::default()),
                "dest_i8",
            )?
            .into_pointer_value();

        let src_i8 = self
            .builder
            .build_bitcast(
                src_ptr,
                self.context.i8_type().ptr_type(AddressSpace::default()),
                "src_i8",
            )?
            .into_pointer_value();

        // Call memcpy
        self.builder.build_call(
            memcpy_intrinsic,
            &[
                dest_i8.into(),
                src_i8.into(),
                copy_size.into(),
                self.context.bool_type().const_int(0, false).into(),
            ],
            "memcpy_call",
        )?;

        // Create new slice struct
        let ptr_type = element_llvm_type.ptr_type(AddressSpace::default());
        let slice_struct_type = self.context.struct_type(
            &[
                ptr_type.into(),
                self.context.i64_type().into(),
                self.context.i64_type().into(),
            ],
            false,
        );

        let slice_alloca = self
            .builder
            .build_alloca(slice_struct_type, "copied_slice")?;

        // Store ptr field
        let ptr_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(0, false),
                ],
                "ptr_field",
            )?
        };
        self.builder.build_store(ptr_field_ptr, new_ptr)?;

        // Store len field
        let len_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(1, false),
                ],
                "len_field",
            )?
        };
        self.builder.build_store(len_field_ptr, src_len)?;

        // Store cap field
        let cap_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(2, false),
                ],
                "cap_field",
            )?
        };
        self.builder.build_store(cap_field_ptr, src_cap)?;

        // Load and return the new slice
        let result_slice =
            self.builder
                .build_load(slice_struct_type, slice_alloca, "result_slice")?;
        Ok(result_slice)
    }
}
